package com.smzd.projforge.business;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.smzd.core.utils.db.SRPage;
import com.smzd.projforge.dao.DynaAttributeDao;
import com.smzd.projforge.dao.DynaClassDao;
import com.smzd.projforge.dao.MTMRelationDao;
import com.smzd.projforge.dao.ProjectDao;
import com.smzd.projforge.dao.RenderDao;
import com.smzd.projforge.dao.SuitDao;
import com.smzd.projforge.dao.TemplateDao;
import com.smzd.projforge.domain.DynaAttribute;
import com.smzd.projforge.domain.DynaClass;
import com.smzd.projforge.domain.MTMRelation;
import com.smzd.projforge.domain.Project;
import com.smzd.projforge.domain.Render;
import com.smzd.projforge.domain.Suit;
import com.smzd.projforge.domain.Template;
import com.smzd.projforge.engine.FMRenderEngine;
import com.smzd.utls.FileUtil;

import freemarker.template.TemplateException;

@Service
public class CodeGeneratorService {
    @Autowired
    ProjectDao projectDao;
    @Autowired
    DynaClassDao clazzDao;
    @Autowired
    DynaAttributeDao attrDao;
    @Autowired
    MTMRelationDao mtmDao;
    @Autowired
    SuitDao suitDao;
    @Autowired
    RenderDao renderDao;
    @Autowired
    TemplateDao templateDao;
    @Autowired
    FMRenderEngine engine;

    public Project generateCode(int projId) throws IOException, TemplateException {
        // 1. （必选）装配所有的设计资源
        Project project = getFullProjectData(projId);
        // 2. （必选）渲染项目，生成代码
        // FreeMarkerUtils.renderProject(project);
        engine.renderProject(project);
        // 3. （可选）生成注册信息
        renderSystemRegister(project);
        return project;
    }

    private void renderSystemRegister(Project project) {
        JSONObject proj = new JSONObject();
        proj.accumulate("id", project.getId());
        proj.accumulate("name", project.getName());
        proj.accumulate("packageName", project.getPackageName());
        proj.accumulate("entry", project.getEntry());
        JSONArray classes = new JSONArray();
        for (DynaClass clazz : project.getClasses()) {
            JSONObject cla = new JSONObject();
            cla.accumulate("id", clazz.getId());
            cla.accumulate("name", clazz.getName());
            classes.add(cla);
        }
        proj.accumulate("classes", classes);
        String content = proj.toString();
        String fileName = ("src\\" + project.getPackageName()).replaceAll("\\.", "\\\\") + "\\mappers\\system.json";
        File file = new File(FMRenderEngine.CODEBASE + project.getName(), fileName);
        FileUtil.saveContent(content, file);
    }

    public Project getFullProjectData(int projectId) {
        // 获取所有的渲染引擎套件
        List<Suit> suits = getAllSuits(projectId);
        Project project = projectDao.getElemById(projectId);
        // 根据ProjectId获得所有的动态类
        List<DynaClass> classes = clazzDao.getClassesByProject(project);
        project.setClasses(classes);
        // 根据ProjectId获得所有的属性
        List<DynaAttribute> props = attrDao.getAttributesByProject(project);
        // 将引用类型的prop.refClass设置为真正的class信息。这样，查询的时候其实不需要查询过多的class信息。
        combineRefClassToAttributes(classes, props);
        for (DynaClass clazz : classes) {
            // 先组装scope
            clazz.setScope(project);
            // 将类和属性组装起来，同时将属性的owner设置为类的详细信息
            combineAttributesToClass(props, clazz);
            // 如果有master主类配置，将其设置为具体的类
            if (clazz.getScopeClass() != null) {
                combimeMasterToClass(classes, clazz);
            }
            if (clazz.getSuit() != null) {
                for (Suit suit : suits) {
                    if (suit.getId().intValue() - clazz.getSuit().getId().intValue() == 0) {
                        clazz.setSuit(suit);
                        break;
                    }
                }
            }
        }
        // 将多对多关系组装起来
        List<MTMRelation> relations = getMTMRelations(project, classes, props);
        project.setMtmRelations(relations);
        return project;
    }

    private List<Suit> getAllSuits(int projectId) {
        List<Suit> suits = suitDao.getAllSuitsByProjectId(projectId);
        List<Render> renders = renderDao.getAllRendersByProjectId(projectId);
        List<Template> templates = templateDao.getAllTemplatesByProjectId(projectId);
        for (Render render : renders) {
            for (Suit suit : suits) {
                if (render.getScope().getId() - suit.getId() == 0) {
                    render.setScope(suit);
                    suit.addRender(render);
                    break;
                }
            }
            for(Template template : templates){
                if(template.getId()-render.getTemplate().getId()==0){
                    render.setTemplate(template);
                    break;
                }
            }
        }
        return suits;
    }

    private void combineRefClassToAttributes(List<DynaClass> classes, List<DynaAttribute> props) {
        for (DynaAttribute attr : props) {
            if (attr.getType() == DynaAttribute.TYPE_DYNACLASS && attr.getRefClass() != null) {
                int refClassId = attr.getRefClass().getId();
                for (DynaClass clazz : classes) {
                    if (refClassId - clazz.getId() == 0) {
                        attr.setRefClass(clazz);
                        break;
                    }
                }
            }
        }
    }

    private void combineAttributesToClass(List<DynaAttribute> props, DynaClass clazz) {
        int classId = clazz.getId();
        List<DynaAttribute> attributes = new ArrayList<DynaAttribute>(20);
        for (DynaAttribute attr : props) {
            if (classId == attr.getScope().getId().intValue()) {
                attr.setScope(clazz);
                attributes.add(attr);
            }
        }
        clazz.setDynaAttributes(attributes);
    }

    private void combimeMasterToClass(List<DynaClass> classes, DynaClass clazz) {
        int scopeClassId = clazz.getScopeClass().getId();
        for (DynaClass scopeClass : classes) {
            if (scopeClass.getId().intValue() == scopeClassId) {
                clazz.setScopeClass(scopeClass);
                break;
            }
        }
    }

    private List<MTMRelation> getMTMRelations(Project project, List<DynaClass> classes, List<DynaAttribute> props) {
        List<MTMRelation> relations = getMTMRelationsByProject(project);
        for (MTMRelation mtm : relations) {
            if (mtm.getMasterField() != null) {// 根据attribute.refClass来设置master/slave
                combineMTMRelationFromAttribute(mtm, classes, props);
            } else {// 根据master/slave的简单信息匹配和设置完整信息
                combineMTMRelationFromClasses(mtm, classes);
            }
            // 完成多对多关系的主类的设置
            mtm.getSlave().addMTMRelation(mtm);
        }
        return relations;
    }

    private List<MTMRelation> getMTMRelationsByProject(Project project) {
        SRPage<Project, MTMRelation> relationPage = new SRPage<>();
        relationPage.setScope(project);
        relationPage.setPageSize(999);
        mtmDao.getElemsByPage(relationPage);
        List<MTMRelation> relations = relationPage.getElems();
        return relations;
    }

    private void combineMTMRelationFromClasses(MTMRelation mtm, List<DynaClass> classes) {
        // 这两个类之间要建立起一个多对多的关系来
        int masterId = mtm.getMaster().getId();
        int slaveId = mtm.getSlave().getId();
        // 现在只有这两个类的ID，查查这两个类到底都是谁
        DynaClass master = null;
        DynaClass slave = null;
        for (DynaClass clazz : classes) {
            int classId = clazz.getId();
            if (classId == masterId) {
                master = clazz;
            }
            if (classId == slaveId) {
                slave = clazz;
            }
            if (master != null && slave != null) {
                break;
            }
        }
        // 验证关系OK
        if (master == null || slave == null) {
            throw new RuntimeException("居然有关系的主从类没都配置好？！");
        }
        mtm.setMaster(master);
        mtm.setSlave(slave);
    }

    /**
     * 通过关系类建立起来的多对多关系
     * 
     * @param mtm
     * @param classes
     * @param props
     */
    private void combineMTMRelationFromAttribute(MTMRelation mtm, List<DynaClass> classes, List<DynaAttribute> props) {
        // 这两个字段所指的类要建立起多对多关系来，关系类是这两个字段所在的类（必须是一个类才行）
        int masterFieldId = mtm.getMasterField().getId();
        int slaveFieldId = mtm.getSlaveField().getId();
        // 现在有了这两个字段的ID了，先查查这两个字段到底是谁，以及它们指向的类是谁。
        DynaClass master = null;
        DynaClass slave = null;
        DynaAttribute masterField = null;
        DynaAttribute slaveField = null;
        for (DynaAttribute attr : props) {
            int attrId = attr.getId();
            if (masterFieldId == attrId) {
                masterField = attr;
                master = masterField.getRefClass();
            }
            // 注意，完全有可能主、从对应关系是同一个类，因此不要用else
            if (slaveFieldId == attrId) {
                slaveField = attr;
                slave = slaveField.getRefClass();
            }
            // 如果两个都找到了，可以停下了。
            if (masterField != null && slaveField != null) {
                break;
            }
        }
        // 验证关系OK
        if (masterField == null || slaveField == null) {
            throw new RuntimeException("居然有关系的主从类属性没都配置好？！");
        }
        // 验证关系OK
        if (master == null || slave == null) {
            throw new RuntimeException("居然有关系的主从类没都配置好？！");
        }
        // 验证关系
        int relationClassId = slaveField.getScope().getId();
        if (masterField.getScope().getId() - relationClassId != 0) {
            throw new RuntimeException("配置多对多关系的两个字段居然不是同一个类？！");
        }
        DynaClass relation = null;// 这个可是关系类哦，别忘了这个
        for (DynaClass clazz : classes) {
            if (clazz.getId() - relationClassId == 0) {
                relation = clazz;
                break;
            }
        }
        if (relation == null) {
            throw new RuntimeException("居然没有找到配置的多对多关系类？！");
        }
        mtm.setRelation(relation);
        mtm.setMasterField(masterField);
        mtm.setSlaveField(slaveField);
        mtm.setMaster(master);
        mtm.setSlave(slave);
    }

}
